﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using MontagsmalerApp.Utils;
using System.Collections.ObjectModel;

namespace MontagsmalerApp
{
	/// <summary>
	/// Löst die Spielerverwaltung aus der Spiel-Klasse. 
	/// Singleton-Pattern.
	/// </summary>
	public class Spielerverwaltung
	{
		#region Singleton
		/// <summary>
		/// Der interne Verweis auf die Spielerverwaltung, die bei Bedarf zurückgeliefert wird, anstatt eine neue Instanz zu erzeugen.
		/// </summary>
		private static Spielerverwaltung instance;

		/// <summary>
		/// Der private Konstruktor für das singleton Objekt. Zur Erzeugeung von außen dient das Property Instance.
		/// </summary>
		private Spielerverwaltung()
		{
			Spielerteams = new ObservableCollection<Spielerteam>();
			AlleSpieler = new List<Einzelspieler>();
		}

		/// <summary>
		/// DIE Spielerverwaltung (singleton).
		/// </summary>
		public static Spielerverwaltung Instance
		{
			get
			{
				if (instance == null)
				{
					instance = new Spielerverwaltung();
				}
				return instance;
			}
		}
		#endregion

		/// <summary>
		/// Alle Teams, die an dem Spiel teilnehmen.
		/// </summary>
		public ObservableCollection<Spielerteam> Spielerteams { get; set; }

		/// <summary>
		/// Alle Spieler, die an dem Spiel teilnehmen.
		/// </summary>
		public IList<Einzelspieler> AlleSpieler { get; set; }

		/// <summary>
		/// Der Spieler, der derzeit malt bzw. nun malen soll.
		/// </summary>
		/// <returns></returns>
		public Spielteilnehmer MalenderSpieler { get; set; }

		/// <summary>
		/// Gibt den Spieler mit dem angegebenen Namen zurück. Wurde er nicht gefunden kommt null zurück.
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public Einzelspieler GetSpielerByName(string name)
		{
			foreach (var einzelspieler in AlleSpieler)
				if (einzelspieler.Name == name)
					return einzelspieler;

			return null;
		}

		/// <summary>
		/// Liefert eine Liste mit Spieler zurück, die einem Team zugeordnet sind.
		/// </summary>
		/// <returns></returns>
		public IList<Einzelspieler> GetAlleSpielerInTeams()
		{
			IList<Einzelspieler> spielerliste = new List<Einzelspieler>();
			foreach (var team in Spielerteams)
				foreach (var teammitglied in team.Einzelspieler)
					spielerliste.Add(teammitglied);

			return spielerliste;
		}

		/// <summary>
		/// Erstellt ein neues Team ohne Mitglieder.
		/// </summary>
		/// <param name="name"></param>
		public void NeuesTeam(string name)
		{
			// already exists?
			foreach (var team in Spielerteams)
				if (team.Name == name)
					throw new Exception("Team existiert bereits:" + name);

			Spielerteams.Add(new Spielerteam(name));
		}

		/// <summary>
		/// Fügt einen Spieler zu einem Team hinzu. Ist der Spieler bereits in einem anderen Team enthalten, so wird er dort entfernt.
		/// </summary>
		/// <param name="spieler"></param>
		/// <param name="team"></param>
		public void SpielerZuweisen(Einzelspieler spieler, Spielerteam team)
		{
			foreach (var otherTeam in Spielerteams)
			{
				if (otherTeam == team)
					continue;

				if (otherTeam.HasSpieler(spieler))
					otherTeam.EntferneSpieler(spieler);

				team.AddSpieler(spieler);
			}
		}

		/// <summary>
		/// Fügt der Spielverwaltung einen neuen Spieler hinzu.
		/// </summary>
		/// <param name="name"></param>
		public void NeuerSpieler(string name)
		{
			foreach (var spieler in AlleSpieler)
				if (spieler.Name == name)
					throw new Exception("Spieler existiert bereits mit dem Namen: " + name);

			AlleSpieler.Add(new Einzelspieler(name));
		}

		/// <summary>
		/// Entfernt den Spieler aus der Spielerverwaltung. Damit ist er auch nicht mehr in einem Team vorhanden.
		/// </summary>
		/// <param name="spieler"></param>
		public void SpielerEntfernen(Einzelspieler spieler)
		{
			foreach (var team in Spielerteams)
				team.EntferneSpieler(spieler);

			AlleSpieler.Remove(spieler);
		}

		/// <summary>
		/// Setzt die Punkte aller Spieler zurück.
		/// </summary>
		public void AllePunkteZuruecksetzen()
		{
			foreach (var spieler in AlleSpieler)
				spieler.PunkteZuruecksetzen();
		}

		/// <summary>
		/// Entfernt den Spieler aus seinem Team.
		/// </summary>
		/// <param name="einzelspieler"></param>
		public void SpielerAusTeamEntfernen(Einzelspieler einzelspieler)
		{
			foreach (var team in Spielerteams)
			{
				if (team.HasSpieler(einzelspieler))
					team.EntferneSpieler(einzelspieler);
			}
		}

		/// <summary>
		/// Löscht das Team. Seine Mitspieler bleiben aber enthalten.
		/// </summary>
		/// <param name="spielerteam"></param>
		public void TeamLoeschen(Spielerteam spielerteam)
		{
			Spielerteams.Remove(spielerteam);
		}

		public Spielerteam GetTeamBySpieler(Einzelspieler spieler)
		{
			Spielerteam result = null;
			foreach (var team in Spielerteams)
			{
				if (team.HasSpieler(spieler))
					result = team;
			}

			return result;
		}

		/// <summary>
		/// Serialisiert das Object in eine Datei.
		/// </summary>
		/// <param name="pfad"></param>
		public void Speichern(string pfad)
		{
			SpielerverwaltungSpeicherobjekt speicherobjekt = new SpielerverwaltungSpeicherobjekt()
			{
				AlleSpieler = AlleSpieler,
				Spielerteams = Spielerteams,
				MalenderSpieler = MalenderSpieler
			};

			using (FileStream fs = new FileStream(pfad, FileMode.Create))
			{
				BinaryFormatter bf = new BinaryFormatter();
				bf.Serialize(fs, speicherobjekt);
				fs.Close();
			}
		}

		/// <summary>
		/// Speichert die Teams an dem intern bekannten Speicherort.
		/// </summary>
		public void Speichern()
		{
			Speichern(ConfigurationParameters.GetTeamSpeicherort());
		}

		/// <summary>
		/// Versucht die angegebene Datei zu laden und sie in eine Spielerverwaltung zu konvertieren. 
		/// </summary>
		/// <param name="pfad"></param>
		/// <returns></returns>
		public void Laden(string pfad)
		{
			SpielerverwaltungSpeicherobjekt speicherobjekt = new SpielerverwaltungSpeicherobjekt();

			using (FileStream fs = new FileStream(pfad, FileMode.Open))
			{
				BinaryFormatter bf = new BinaryFormatter();
				object obj = bf.Deserialize(fs);
				if (obj is SpielerverwaltungSpeicherobjekt)
				{
					speicherobjekt = (SpielerverwaltungSpeicherobjekt)obj;
					instance.AlleSpieler = speicherobjekt.AlleSpieler;
					instance.Spielerteams = speicherobjekt.Spielerteams;
					instance.MalenderSpieler = speicherobjekt.MalenderSpieler;
				}
			}
		}

		/// <summary>
		/// Lädt die Teams aus von dem intern bekannten Speicherort.
		/// </summary>
		public void Laden()
		{
			Laden(ConfigurationParameters.GetTeamSpeicherort());
		}

		/// <summary>
		/// Gibt anhand der aktuellen Punkte die Platzierungen der Teams wieder.
		/// </summary>
		/// <returns></returns>
		public List<Platzierung> GetPlatzierungen()
		{
			List<Platzierung> platzierungen = new List<Platzierung>();
			List<Spielerteam> teamsNachPunktenGeordnet = Spielerteams.OrderByDescending(c => c.GetAktuellePunkte()).ToList();
			for (int i = 0; i < teamsNachPunktenGeordnet.Count; i++)
			{
				Spielerteam currentTeam = teamsNachPunktenGeordnet[i];
				platzierungen.Add(new Platzierung(i + 1, currentTeam.Name, currentTeam.GetAktuellePunkte()));
			}
			return platzierungen;
		}
	}
}

